/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "ModelInHardware.h"
//#include <Rnd.h> // BUG


namespace liba
{
namespace lib3d
{
namespace model
{

ModelInHardwareObject::ModelInHardwareObject(hard::VertexBuffer * vb, hard::IndexBuffer * ib)
:	vb( vb ),
	ib( ib )
{}

ModelInHardwareObject::~ModelInHardwareObject()
{
	delete vb; vb = 0;
	delete ib; ib = 0;
}


ModelInHardware::ModelInHardware(const filesystem::String & name)
:	total_primitives( 0 ),
	total_vertices( 0 ),
	model_name(name)
{
	load_rep( name );
	hard::Manager::add_dependent( this );
}

ModelInHardware::~ModelInHardware()
{
	hard::Manager::remove_dependent( this );
}

void ModelInHardware::on_add_hardware(hard::Manager::hardware_type * ha)
{
	hard_vector.push_back( SweetPair(ha, new ModelInHardwareObject(ha->create_vertex_buffer(), 0)) );
	start_loading();
}

/*bool ModelInHardware::render(const material::Shaders & shaders, hard::Hardware * ha, const CameraDesc & camera_desc)
{
	ModelInHardwareObject * object = find_data(ha);
	if( !object->vb->is_valid() ||
		(object->ib && !object->ib->is_valid()) )
	{
		start_loading();
		return false;
	}
	material::Shaders::const_iterator sit = shaders.begin();
	int pos = 0;
	if( object->ib )
		for( ; sit != shaders.end(); ++sit )
		{
			int count = (*sit)->get_triangles_count();
			(*sit)->set_in_hardware( ha, camera_desc );
			object->vb->render( object->ib, pos, count );
			pos += count*3;
		}
	else
		for( ; sit != shaders.end(); ++sit )
		{
			int count = (*sit)->get_triangles_count();
			(*sit)->set_in_hardware( ha, camera_desc );
			object->vb->render( pos, count );
			pos += count*3;
		}
	return true;
}*/

void ModelInHardware::render_to_list(const material::Shaders & shaders, hard::Hardware * hardware, hard::RenderList * render_list, const hard::RenderModel & model_ref)
{
	ModelInHardwareObject * object = find_data( hardware );
	if( !object->vb->is_valid() ||
		(object->ib && !object->ib->is_valid()) )
	{
		start_loading();
		return;
	}
	material::Shaders::const_iterator sit = shaders.begin();
	int primitive_pos = 0;
	int vertex_pos = 0;
	for( ; sit != shaders.end(); ++sit )
	{
		if((*sit)->get_transparency() > 0.99)
			continue;
		
		int primitive_count = (*sit)->get_triangles_count();
		int vertex_count = (*sit)->get_vertex_count();
		
		hard::RenderModel render_model;
		render_model.world = model_ref.world;
//		render_model.vb2 = model_ref.vb2;	//TODO: currently unused
		
		render_model.vb = object->vb;
		render_model.bounding_sphere = bounding_sphere;
		render_model.primitive_start = primitive_pos;
		render_model.primitive_count = primitive_count;
		render_model.ib = object->ib;
		if( object->ib )
		{
			render_model.vertex_start = vertex_pos;
			render_model.vertex_count = vertex_count;
		}

		(*sit)->render_to_list( hardware, &render_model.material );
		// TODO - add texture transparency criteria
		render_list->models.push_back( render_model );
		primitive_pos += primitive_count*3;
		vertex_pos += vertex_count;
	}
}

void ModelInHardware::render_to_shadow_list(const material::Shaders & shaders, hard::Hardware * hardware, hard::RenderList * render_list, const hard::RenderModel & model_ref)
{
#ifdef WIN_VERSION
	ModelInHardwareObject * object = find_data( hardware );
	if( !object->vb->is_valid() ||
		(object->ib && !object->ib->is_valid()) )
	{
		start_loading();
		return;
	}
	material::Shaders::const_iterator sit = shaders.begin();

	int count = (*sit)->get_triangles_count();

	hard::RenderModel render_model( model_ref );
	render_model.vb = object->vb;
	render_model.bounding_sphere = bounding_sphere;
	render_model.primitive_start = 0;
	render_model.primitive_count = total_primitives;
	render_model.ib = object->ib;
	render_model.vertex_start = 0;
	render_model.vertex_count = total_vertices;

	(*sit)->render_to_list( hardware, &render_model.material );

	render_list->shadow_models.push_back( render_model );
#endif
}

void ModelInHardware::fill_vb2(hard::Hardware * hardware, hard::RenderModel * model_ref)
{
	model_ref->vb2 = 0;
	ModelInHardwareObject * object = find_data( hardware );
	if( !object->vb->is_valid() ||
		(object->ib && !object->ib->is_valid()) )
	{
		start_loading();
		return;
	}
	model_ref->vb2 = object->vb;
}

void ModelInHardware::load(const filesystem::String & name)
{
	if( !get_rep()->is_loaded() )
		return;
		
	const SimpleArray<float> * positions = get_rep()->find_value_float( Model::position_name );
	const SimpleArray<float> * normals = get_rep()->find_value_float( Model::normal_name );
	const SimpleArray<float> * face_normals = get_rep()->find_value_float( Model::face_normal_name );
	const SimpleArray<float> * u_tangents = get_rep()->find_value_float( Model::u_tangent_name );
	const SimpleArray<float> * v_tangents = get_rep()->find_value_float( Model::v_tangent_name );

	const SimpleArray<float> * uv0 = get_rep()->find_value_float( Model::uv0_name );
	const SimpleArray<float> * uv1 = get_rep()->find_value_float( Model::uv1_name );

	const SimpleArray<float> * weights0 = get_rep()->find_value_float( Model::weights0_name );
	const SimpleArray<float> * weights1 = get_rep()->find_value_float( Model::weights1_name );
	const SimpleArray<float> * weights2 = get_rep()->find_value_float( Model::weights2_name );

	const SimpleArray<int> * indices0 = get_rep()->find_value_int( Model::indices0_name );
	const SimpleArray<int> * indices1 = get_rep()->find_value_int( Model::indices1_name );
	const SimpleArray<int> * indices2 = get_rep()->find_value_int( Model::indices2_name );
	const SimpleArray<int> * indices3 = get_rep()->find_value_int( Model::indices3_name );

	const SimpleArray<int> * index_array = get_rep()->find_value_int( Model::index_name );

	total_vertices = positions->size() / 3;
	if( index_array->size() )
		total_primitives = index_array->size() / 3;
	else
		total_primitives = positions->size() / 9;

	HardVector::iterator it = hard_vector.begin();
	for( ;it != hard_vector.end(); ++it )
	{
		if(!it->first->is_operational())
			continue;

		ModelInHardwareObject * object = it->second;
		if( !object->vb->is_valid() )
		{
			if( !object->vb->update( *positions, *normals,
								*u_tangents, *v_tangents,
								*uv0, *uv1,
								*weights0, *weights1, *weights2,
								*indices0, *indices1, *indices2, *indices3) )
			{
				THROW(GluckBadResource(__FUNCTION__, "object->vb->update() failed", name ));
			}
		}
		if( index_array->size() && !object->ib )
			object->ib = it->first->create_index_buffer();
		if( object->ib && !object->ib->is_valid() )
		{
			if( !object->ib->update( index_array->data(), index_array->size() ) )
				THROW(GluckBadResource(__FUNCTION__, "object->ib->update() failed", name ));
		}
	}

	bounding_sphere = get_rep()->get_bounding_sphere();
	
	stop_loading();
}

void ModelInHardware::start_loading()
{
#ifndef WIN_VERSION
	//load_rep(model_name);
#endif
	
	SharedPrecachedRepBase::start_loading();
}

void ModelInHardware::stop_loading()
{
#ifndef WIN_VERSION
	//unload_rep();
#endif

	SharedPrecachedRepBase::stop_loading();
}

} // namespace model
} // namespace lib3d
} // namespace liba

